﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using NAudio;
using NAudio.Wave;

namespace NotifierService.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class EasyService : ServiceBase, IDisposable
    {

        #region Fields

        //Realtime broadcast support.
        bool _broadcastRunning = false;
        IWavePlayer _waveOut;
        BufferedWaveProvider _waveProvider;
        Codec.IAudioCodec _audioCodec;

        IPAddress _clientIPAddr;
        IPEndPoint _clientEndPoint;
        bool _isAuthenticated = false;

        #endregion

        #region Constructor&Destructor

        public EasyService()
        {
            _audioCodec = new Codec.UltraWideBandSpeexCodec();
            OperationContext.Current.Channel.Closed += Channel_Closed;
        }


        ~EasyService()
        {
            Dispose(false);
        }
        #endregion

        #region Events


        private void Channel_Closed(object sender, EventArgs e)
        {
            OnClosed();
            //throw new NotImplementedException();
        }

        #endregion


        public override bool Authenticate(string name, string password)
        {
            if (name == "admin" && password == "admin")
            {
                _isAuthenticated = true;

                //Get client ip addr
                var context = OperationContext.Current;
                var properties = context.IncomingMessageProperties;
                var endpointProperty = properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
                _clientIPAddr = IPAddress.Parse(endpointProperty.Address);
                _clientEndPoint = new IPEndPoint(_clientIPAddr, endpointProperty.Port);
            }
            else
                _isAuthenticated = false;

            return _isAuthenticated;
        }

        public override void Close()
        {
            this.Dispose();
        }

        public override IPAddress GetIPAddress()
        {
            return _clientIPAddr;   
        }

        public override bool PlayAudio(AudioType type)
        {
            Console.WriteLine("Audio played.");
            return true;
        }

        public override RealtimeBroadcastServiceData StartRealtimeBroadcast()
        {
            if (!_isAuthenticated) return new RealtimeBroadcastServiceData() { Succeed = false };
            if (_broadcastRunning) return new RealtimeBroadcastServiceData() { Succeed = true, IsIPv6 = _clientEndPoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6, Port = _clientEndPoint.Port };

            if (_waveOut != null)
            {
                try
                {
                    _waveOut.Dispose();
                    _waveOut = null;
                }
                catch { }
            }

            _waveProvider = null;

            try
            {
                _waveOut = new WaveOut();
                _waveProvider = new BufferedWaveProvider(_audioCodec.RecordFormat);
                _waveOut.Init(_waveProvider);
                _waveOut.Play();
            }
            catch
            {
                if (_waveOut != null)
                    _waveOut.Dispose();
                _waveOut = null;
                _waveProvider = null;
            }

            RealtimeBroadcastServiceV4.GetInstance().AddService(this);
            RealtimeBroadcastServiceV6.GetInstance().AddService(this);

            _broadcastRunning = true;

            return new RealtimeBroadcastServiceData() { Succeed = true, IsIPv6 = _clientEndPoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6, Port = _clientEndPoint.Port }; ;
        }

        public override bool StopRealtimeBroadcast()
        {
            if (!_broadcastRunning) return false;

            RealtimeBroadcastServiceV4.GetInstance().RemoveService(this);
            RealtimeBroadcastServiceV6.GetInstance().RemoveService(this);

            try
            {
                _waveOut.Dispose();
                _waveOut = null;
                _waveProvider = null;
            }
            catch { }

            _broadcastRunning = false;

            return true;
        }

        protected override void ProcessIncomingVoiceStream(byte[] buffer, int length)
        {
            byte[] decoded = _audioCodec.Decode(buffer, 0, length);
            _waveProvider.AddSamples(decoded, 0, decoded.Length);
        }


        #region private methods

        private void OnClosed()
        {
            if (_broadcastRunning)
            {
                StopRealtimeBroadcast();
            }
        }

        #endregion


        #region IDisposable Impl

        bool _isDisposed = false;


        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_isDisposed)
            {
                if (disposing)
                {
                    if (_waveOut != null)
                        _waveOut.Dispose();
                }

                //release all unmanaged resource.
                GC.SuppressFinalize(this);
            }
            _isDisposed = true;
        }

        public override IPEndPoint GetClientEndpoint()
        {
            return _clientEndPoint;
        }

        public override string GetServiceName()
        {
            Console.WriteLine("Cilent called ::GetServiceName().");
            return Environment.UserName;
        }


        #endregion

    }
}
